// Copyright 2018 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef LIB_FPROMISE_PROMISE_H_
#define LIB_FPROMISE_PROMISE_H_

#include <assert.h>
#include <lib/fit/function.h>
#include <lib/stdcompat/variant.h>

#include <tuple>
#include <type_traits>
#include <utility>

#include "promise_internal.h"
#include "result.h"

namespace fpromise {

// A |fpromise::promise| is a building block for asynchronous control flow that
// wraps an asynchronous task in the form of a "continuation" that is
// repeatedly invoked by an executor until it produces a result.
//
// Additional asynchronous tasks can be chained onto the promise using
// a variety of combinators such as |then()|.
//
// Use |fpromise::make_promise()| to create a promise.
// Use |fpromise::make_ok_promise()| to create a promise that immediately returns a value.
// Use |fpromise::make_error_promise()| to create a promise that immediately returns an error.
// Use |fpromise::make_result_promise()| to create a promise that immediately returns a result.
// Use |fpromise::future| to more conveniently hold a promise or its result.
// Use |fpromise::pending_task| to wrap a promise as a pending task for execution.
// Use |fpromise::executor| to execute a pending task.
// See examples below.
//
// Always look to the future; never look back.
//
// SYNOPSIS
//
// |V| is the type of value produced when the completes successfully.
// Defaults to |void|.
//
// |E| is the type of error produced when the completes with an error.
// Defaults to |void|.
//
// Class members are documented in |fpromise::promise_impl|.
//
// CHAINING PROMISES USING COMBINATORS
//
// Promises can be chained together using combinators such as |then()|
// which consume the original promise(s) and return a new combined promise.
//
// For example, the |then()| combinator returns a promise that has the effect
// of asynchronously awaiting completion of the prior promise (the instance
// upon which |then()| was called) then delivering its result to a handler
// function.
//
// Available combinators defined in this library:
//
//    |then()|: run a handler when prior promise completes
//    |and_then()|: run a handler when prior promise completes successfully
//    |or_else()|: run a handler when prior promise completes with an error
//    |inspect()|: examine result of prior promise
//    |discard_result()|: discard result and unconditionally return
//                        fpromise::result<> when prior promise completes
//    |discard_value()|: discard value and return fpromise::result<void, E>
//                       when prior promise completes
//    |wrap_with()|: applies a wrapper to the promise
//    |box()|: wraps the promise's continuation into a |fit::function|
//    |fpromise::join_promises()|: await multiple promises in an argument list,
//                            once they all complete return a tuple of
//                            their results
//    |fpromise::join_promise_vector()|: await multiple promises in a vector,
//                                  once they all complete return a vector
//                                  of their results
//
// You can also create your own custom combinators by crafting new
// types of continuations.
//
// CONTINUATIONS AND HANDLERS
//
// Internally, |fpromise::promise| wraps a continuation (a kind of callable
// object) that holds the state of the asynchronous task and provides a
// means for making progress through repeated invocation.
//
// A promise's continuation is generated through the use of factories
// such as |make_promise()| and combinators such as |then()|.  Most of
// these functions accept a client-supplied "handler" (another kind
// of callable object, often a lambda expression) which performs the actual
// computations.
//
// Continuations have a very regular interface: they always accept a
// |fpromise::context&| argument and return a |fpromise::result|.  Conversely, handlers
// have a very flexible interface: clients can provide them in many forms
// all of which are documented by the individual functions which consume them.
// It's pretty easy to use: the library takes care of wrapping client-supplied
// handlers of all supported forms into the continuations it uses internally.
//
// THEORY OF OPERATION
//
// On its own, a promise is "inert"; it only makes progress in response to
// actions taken by its owner.  The state of the promise never changes
// spontaneously or concurrently.
//
// Typically, a promise is executed by wrapping it into a |fpromise::pending_task|
// and scheduling it for execution using |fpromise::executor::schedule_task()|.
// A promise's |operator(fpromise::context&)| can also be invoked directly by its owner
// from within the scope of another task (this is used to implement combinators
// and futures) though the principle is the same.
//
// |fpromise::executor| is an abstract class that encapsulates a strategy for
// executing tasks.  The executor is responsible for invoking each tasks's
// continuation until the task returns a non-pending result, indicating that
// the task has been completed.
//
// The method of execution and scheduling of each continuation call is left
// to the discretion of each executor implementation.  Typical executor
// implementations may dispatch tasks on an event-driven message loop or on
// a thread pool.  Developers are responsible for selecting appropriate
// executor implementations for their programs.
//
// During each invocation, the executor passes the continuation an execution
// context object represented by a subclass of |fpromise::context|.  The continuation
// attempts to make progress then returns a value of type |fpromise::result| to
// indicate whether it completed successfully (signaled by |fpromise::ok()|),
// failed with an error (signaled by |fpromise::error()|, or was unable to complete
// the task during that invocation (signaled by |fpromise::pending()|).
// For example, a continuation may be unable to complete the task if it must
// asynchronously await completion of an I/O or IPC operation before it
// can proceed any further.
//
// If the continuation was unable to complete the task during its invocation,
// it may to call |fpromise::context::suspend_task()| to acquire a
// |fpromise::suspended_task| object.  The continuation then arranges for the
// task to be resumed asynchronously (with |fpromise::suspended_task::resume_task()|)
// once it becomes possible for the promise to make forward progress again.
// Finally, the continuation returns returns |fpromise::pending()| to indicate to
// the executor that it was unable to complete the task during that invocation.
//
// When the executor receives a pending result from a task's continuation,
// it moves the task into a table of suspended tasks.  A suspended task
// is considered abandoned if has not been resume and all remaining
// |fpromise::suspended_task| handles representing it have been dropped.
// When a task is abandoned, the executor removes it from its table of
// suspended tasks and destroys the task because it is not possible for the task
// to be resumed or to make progress from that state.
//
// See also |fpromise::single_threaded_executor| for a simple executor implementation.
//
// BOXED AND UNBOXED PROMISES
//
// To make combination and execution as efficient as possible, the promises
// returned by |fpromise::make_promise| and by combinators are parameterized by
// complicated continuation types that are hard to describe, often consisting of
// nested templates and lambdas.  These are referred to as "unboxed"
// promises.  In contrast, "boxed" promises are parameterized by a
// |fit::function| that hides (or "erases") the type of the continuation
// thereby yielding type that is easier to describe.
//
// You can recognize boxed and unboxed promises by their types.
// Here are two examples:
//
// - A boxed promise type: `fpromise::promise<void, void>` which is an alias for
//  `fpromise::promise_impl<void, void, std::function<fpromise::result<void, void>>`.
// - An unboxed promise type: `fpromise::promise_impl<void, void,
//   fpromise::internal::then_continuation<...something unintelligible...>>`
//
// Although boxed promises are easier to manipulate, they may cause the
// continuation to be allocated on the heap.  Chaining boxed promises can
// result in multiple allocations being produced.
//
// Conversely, unboxed promises have full type information.  Not only does
// this defer heap allocation but it also makes it easier for the C++
// compiler to fuse a chains of unboxed promises together into a single
// object that is easier to optimize.
//
// Unboxed promises can be boxed by assigning them to a boxed promise
// type (such as |fpromise::promise<>|) or using the |box()| combinator.
//
// As a rule of thumb, always defer boxing of promises until it is necessary
// to transport them using a simpler type.
//
// Do this: (chaining as a single expression performs at most one heap allocation)
//
//     fpromise::promise<> f = fpromise::make_promise([] { ... });
//         .then([](fpromise::result<>& result) { ... });
//         .and_then([] { ... });
//
// Or this: (still only performs at most one heap allocation)
//
//     auto f = fpromise::make_promise([] { ... });
//     auto g = f.then([](fpromise::result<>& result) { ... });
//     auto h = g.and_then([] { ... });
//     fpromise::promise<> boxed_h = h;
//
// But don't do this: (incurs up to three heap allocations due to eager boxing)
//
//     fpromise::promise<> f = fpromise::make_promise([] { ... });
//     fpromise::promise<> g = f.then([](fpromise::result<>& result) { ... });
//     fpromise::promise<> h = g.and_then([] { ... });
//
// SINGLE OWNERSHIP MODEL
//
// Promises have single-ownership semantics.  This means that there
// can only be at most one reference to the task represented by its
// continuation along with any state held by that continuation.
//
// When a combinator is applied to a promise, ownership of its continuation
// is transferred to the combined promise, leaving the original promise
// in an "empty" state without a continuation.  Note that it is an error
// to attempt to invoke an empty promise (will assert at runtime).
//
// This model greatly simplifies reasoning about object lifetime.
// If a promise goes out of scope without completing its task, the task
// is considered "abandoned", causing all associated state to be destroyed.
//
// Note that a promise may capture references to other objects whose lifetime
// differs from that of the promise.  It is the responsibility of the promise
// to ensure reachability of the objects whose reference it captures such
// as by using reference counted pointers, weak pointers, or other appropriate
// mechanisms to ensure memory safety.
//
// THREADING MODEL
//
// Promise objects are not thread-safe themselves.  You cannot call their
// methods concurrently (or re-entrantly).  However, promises can safely
// be moved to other threads and executed there (unless their continuation
// requires thread affinity for some reason but that's beyond the scope
// of this document).
//
// This property of being thread-independent, combined with the single
// ownership model, greatly simplifies the implementation of thread pool
// based executors.
//
// RESULT RETENTION AND FIT::FUTURES
//
// A promise's continuation can only be executed to completion once.
// After it completes, it cannot be run again.
//
// This method of execution is very efficient; the promise's result is returned
// directly to its invoker; it is not copied or retained within the promise
// object itself.  It is entirely the caller's responsibility to decide how to
// consume or retain the result if need be.
//
// For example, the caller can move the promise into a |fpromise::future| to
// more conveniently hold either the promise or its result upon completion.
//
// CLARIFICATION OF NOMENCLATURE
//
// In this library, the words "promise" and "future" have the following
// definitions:
//
// - A *promise* holds the function that performs an asynchronous task.
//   It is the means to produce a value.
// - A *future* holds the value produced by an asynchronous task or a
//   promise to produce that value if the task has not yet completed.
//   It is a proxy for a value that is to be computed.
//
// Be aware that other libraries may use these terms slightly differently.
//
// For more information about the theory of futures and promises, see
// https://en.wikipedia.org/wiki/Futures_and_promises.
//
// COMPARISON WITH STD::FUTURE
//
// |std::future| provides a mechanism for running asynchronous tasks
// and awaiting their results on other threads.  Waiting can be performed
// either by blocking the waiting thread or by polling the future.
// The manner in which tasks are scheduled and executed is entirely
// controlled by the C++ standard library and offers limited control
// to developers.
//
// |fpromise::promise| and |fpromise::future| provide a mechanism for running asynchronous
// tasks, chaining additional tasks using combinators, and awaiting their
// results.  An executor is responsible for suspending tasks awaiting
// results of other tasks and is at liberty to run other tasks on the
// same thread rather than blocking.  In addition, developers can create custom
// executors to implement their own policies for running tasks.
//
// Decoupling awaiting from blocking makes |fpromise::promise| quite versatile.
// |fpromise::promise| can also interoperate with other task dispatching mechanisms
// (including |std::future|) using adapters such as |fpromise::bridge|.
//
// EXAMPLE
//
// -
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/utest/fit/examples/promise_example1.cc
// -
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/utest/fit/examples/promise_example2.cc
//
template <typename V = void, typename E = void>
using promise = promise_impl<::fit::function<result<V, E>(fpromise::context&)>>;

// Promise implementation details.
// See |fpromise::promise| documentation for more information.
template <typename Continuation>
class promise_impl final {
  static_assert(::fpromise::internal::is_continuation<Continuation>::value,
                "Continuation type is invalid.  A continuation is a callable object "
                "with this signature: fpromise::result<V, E>(fpromise::context&).");

  using state_type = ::fit::nullable<Continuation>;

 public:
  // The type of callable object held by the promise.
  // Its signature is: result_type(fpromise::context&).
  using continuation_type = Continuation;

  // The promise's result type.
  // Equivalent to fpromise::result<value_type, error_type>.
  using result_type = typename ::fpromise::internal::continuation_traits<Continuation>::result_type;

  // The type of value produced when the promise completes successfully.
  // May be void.
  using value_type = typename result_type::value_type;

  // The type of value produced when the promise completes with an error.
  // May be void.
  using error_type = typename result_type::error_type;

  // Creates an empty promise without a continuation.
  // A continuation must be assigned before the promise can be used.
  promise_impl() = default;
  explicit promise_impl(decltype(nullptr)) {}

  promise_impl(const promise_impl&) = delete;
  promise_impl& operator=(const promise_impl&) = delete;

  // Constructs the promise by taking the continuation from another promise,
  // leaving the other promise empty.
  promise_impl(promise_impl&& other) : state_{std::move(other.state_)} { other.state_.reset(); }

  // Assigns the promise by taking the continuation from another promise,
  // leaving the other promise empty.
  promise_impl& operator=(promise_impl&& other) {
    if (this != &other) {
      state_ = std::move(other.state_);
      other.state_.reset();
    }
    return *this;
  }

  // Creates a promise with a continuation.
  // If |continuation| equals nullptr then the promise is empty.
  explicit promise_impl(continuation_type continuation) : state_(std::move(continuation)) {}

  // Converts from a promise holding a continuation that is assignable to
  // to this promise's continuation type.
  //
  // This is typically used to create a promise with a boxed continuation
  // type (such as |fit::function|) from an unboxed promise produced by
  // |fpromise::make_promise| or by combinators.
  //
  // EXAMPLE
  //
  //     // f is a promise_impl with a complicated unboxed type
  //     auto f = fpromise::make_promise([] { ... });
  //
  //     // g wraps f's continuation
  //     fpromise::promise<> g = std::move(f);
  //
  template <
      typename OtherContinuation,
      std::enable_if_t<!std::is_same<continuation_type, OtherContinuation>::value &&
                           std::is_constructible<continuation_type, OtherContinuation&&>::value,
                       bool> = true>
  promise_impl(promise_impl<OtherContinuation> other)
      : state_(other.state_.has_value() ? state_type(continuation_type(std::move(*other.state_)))
                                        : state_type()) {}

  // Destroys the promise, releasing its continuation.
  ~promise_impl() = default;

  // Returns true if the promise is non-empty (has a valid continuation).
  explicit operator bool() const { return state_.has_value(); }

  // Invokes the promise's continuation.
  //
  // This method should be called by an executor to evaluate the promise.
  // If the result's state is |result_state::pending| then the executor
  // is responsible for arranging to invoke the promise's continuation
  // again once it determines that it is possible to make progress
  // towards completion of the promise encapsulated within the promise.
  //
  // Once the continuation returns a result with status |result_state::ok|
  // or |result_state::error|, the promise is assigned an empty continuation.
  //
  // Asserts that the promise is non-empty.
  result_type operator()(context& context) {
    result_type result = (state_.value())(context);
    if (!result.is_pending())
      state_.reset();
    return result;
  }

  // Takes the promise's continuation, leaving it in an empty state.
  // Asserts that the promise is non-empty.
  continuation_type take_continuation() {
    auto continuation = std::move(state_.value());
    state_.reset();
    return continuation;
  }

  // Discards the promise's continuation, leaving it empty.
  promise_impl& operator=(decltype(nullptr)) {
    state_.reset();
    return *this;
  }

  // Assigns the promise's continuation.
  promise_impl& operator=(continuation_type continuation) {
    state_ = std::move(continuation);
    return *this;
  }

  // Swaps the promises' continuations.
  void swap(promise_impl& other) {
    using std::swap;
    swap(state_, other.state_);
  }

  // Returns an unboxed promise which invokes the specified handler
  // function after this promise completes (successfully or unsuccessfully),
  // passing its result.
  //
  // The received result's state is guaranteed to be either
  // |fpromise::result_state::ok| or |fpromise::result_state::error|, never
  // |fpromise::result_state::pending|.
  //
  // |handler| is a callable object (such as a lambda) which consumes the
  // result of this promise and returns a new result with any value type
  // and error type.  Must not be null.
  //
  // The handler must return one of the following types:
  // - void
  // - fpromise::result<new_value_type, new_error_type>
  // - fpromise::ok<new_value_type>
  // - fpromise::error<new_error_type>
  // - fpromise::pending
  // - fpromise::promise<new_value_type, new_error_type>
  // - any callable or unboxed promise with the following signature:
  //   fpromise::result<new_value_type, new_error_type>(fpromise::context&)
  //
  // The handler must accept one of the following argument lists:
  // - (result_type&)
  // - (const result_type&)
  // - (fpromise::context&, result_type&)
  // - (fpromise::context&, const result_type&)
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .then([] (fpromise::result<int, std::string>& result)
  //                   -> fpromise::result<std::string, void> {
  //             if (result.is_ok()) {
  //                 printf("received value: %d\n", result.value());
  //                 if (result.value() % 15 == 0)
  //                     return ::fpromise::ok("fizzbuzz");
  //                 if (result.value() % 3 == 0)
  //                     return ::fpromise::ok("fizz");
  //                 if (result.value() % 5 == 0)
  //                     return ::fpromise::ok("buzz");
  //                 return ::fpromise::ok(std::to_string(result.value()));
  //             } else {
  //                 printf("received error: %s\n", result.error().c_str());
  //                 return ::fpromise::error();
  //             }
  //         })
  //         .then(...);
  //
  template <typename ResultHandler>
  promise_impl<::fpromise::internal::then_continuation<promise_impl, ResultHandler>> then(
      ResultHandler handler) {
    static_assert(::fit::is_callable<ResultHandler>::value,
                  "ResultHandler must be a callable object.");

    assert(!fit::is_null(handler));
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::then_continuation<promise_impl, ResultHandler>(std::move(*this),
                                                                             std::move(handler)));
  }

  // Returns an unboxed promise which invokes the specified handler
  // function after this promise completes successfully, passing its
  // resulting value.
  //
  // |handler| is a callable object (such as a lambda) which consumes the
  // result of this promise and returns a new result with any value type
  // but the same error type.  Must not be null.
  //
  // The handler must return one of the following types:
  // - void
  // - fpromise::result<new_value_type, error_type>
  // - fpromise::ok<new_value_type>
  // - fpromise::error<error_type>
  // - fpromise::pending
  // - fpromise::promise<new_value_type, error_type>
  // - any callable or unboxed promise with the following signature:
  //   fpromise::result<new_value_type, error_type>(fpromise::context&)
  //
  // The handler must accept one of the following argument lists:
  // - (value_type&)
  // - (const value_type&)
  // - (fpromise::context&, value_type&)
  // - (fpromise::context&, const value_type&)
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .and_then([] (const int& value) {
  //             printf("received value: %d\n", value);
  //             if (value % 15 == 0)
  //                 return ::fpromise::ok("fizzbuzz");
  //             if (value % 3 == 0)
  //                 return ::fpromise::ok("fizz");
  //             if (value % 5 == 0)
  //                 return ::fpromise::ok("buzz");
  //             return ::fpromise::ok(std::to_string(value));
  //         })
  //         .then(...);
  //
  template <typename ValueHandler>
  promise_impl<::fpromise::internal::and_then_continuation<promise_impl, ValueHandler>> and_then(
      ValueHandler handler) {
    static_assert(::fit::is_callable<ValueHandler>::value,
                  "ValueHandler must be a callable object.");

    assert(!fit::is_null(handler));
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::and_then_continuation<promise_impl, ValueHandler>(
            std::move(*this), std::move(handler)));
  }

  // Returns an unboxed promise which invokes the specified handler
  // function after this promise completes with an error, passing its
  // resulting error.
  //
  // |handler| is a callable object (such as a lambda) which consumes the
  // result of this promise and returns a new result with any error type
  // but the same value type.  Must not be null.
  //
  // The handler must return one of the following types:
  // - void
  // - fpromise::result<value_type, new_error_type>
  // - fpromise::ok<value_type>
  // - fpromise::error<new_error_type>
  // - fpromise::pending
  // - fpromise::promise<value_type, new_error_type>
  // - any callable or unboxed promise with the following signature:
  //   fpromise::result<value_type, new_error_type>(fpromise::context&)
  //
  // The handler must accept one of the following argument lists:
  // - (error_type&)
  // - (const error_type&)
  // - (fpromise::context&, error_type&)
  // - (fpromise::context&, const error_type&)
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .or_else([] (const std::string& error) {
  //             printf("received error: %s\n", error.c_str());
  //             return ::fpromise::error();
  //         })
  //         .then(...);
  //
  template <typename ErrorHandler>
  promise_impl<::fpromise::internal::or_else_continuation<promise_impl, ErrorHandler>> or_else(
      ErrorHandler handler) {
    static_assert(::fit::is_callable<ErrorHandler>::value,
                  "ErrorHandler must be a callable object.");

    assert(!fit::is_null(handler));
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::or_else_continuation<promise_impl, ErrorHandler>(std::move(*this),
                                                                               std::move(handler)));
  }

  // Returns an unboxed promise which invokes the specified handler
  // function after this promise completes (successfully or unsuccessfully),
  // passing it the promise's result then delivering the result onwards
  // to the next promise once the handler returns.
  //
  // The handler receives a const reference, or non-const reference
  // depending on the signature of the handler's last argument.
  //
  // - Const references are especially useful for inspecting a
  //   result mid-stream without modification, such as printing it for
  //   debugging.
  // - Non-const references are especially useful for synchronously
  //   modifying a result mid-stream, such as clamping its bounds or
  //   injecting a default value.
  //
  // |handler| is a callable object (such as a lambda) which can examine
  // or modify the incoming result.  Unlike |then()|, the handler does
  // not need to propagate the result onwards.  Must not be null.
  //
  // The handler must return one of the following types:
  // - void
  //
  // The handler must accept one of the following argument lists:
  // - (result_type&)
  // - (const result_type&)
  // - (fpromise::context&, result_type&)
  // - (fpromise::context&, const result_type&)
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .inspect([] (const fpromise::result<int, std::string>& result) {
  //             if (result.is_ok())
  //                 printf("received value: %d\n", result.value());
  //             else
  //                 printf("received error: %s\n", result.error().c_str());
  //         })
  //         .then(...);
  //
  template <typename InspectHandler>
  promise_impl<::fpromise::internal::inspect_continuation<promise_impl, InspectHandler>> inspect(
      InspectHandler handler) {
    static_assert(::fit::is_callable<InspectHandler>::value,
                  "InspectHandler must be a callable object.");
    static_assert(std::is_void<typename ::fit::callable_traits<InspectHandler>::return_type>::value,
                  "InspectHandler must return void.");

    assert(!fit::is_null(handler));
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::inspect_continuation<promise_impl, InspectHandler>(
            std::move(*this), std::move(handler)));
  }

  // Returns an unboxed promise which discards the result of this promise
  // once it completes, thereby always producing a successful result of
  // type fpromise::result<void, void> regardless of whether this promise
  // succeeded or failed.
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .discard_result()
  //         .then(...);
  //
  promise_impl<::fpromise::internal::discard_result_continuation<promise_impl>> discard_result() {
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::discard_result_continuation<promise_impl>(std::move(*this)));
  }

  // Returns an unboxed promise which discards the value of this promise
  // once it completes, thereby always producing a result of type
  // fpromise::result<void, E> regardless of whether this promise
  // succeeded or failed.
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     auto f = fpromise::make_promise(...)
  //         .discard_value()
  //         .and_then(...)
  //         .or_else(...);
  //
  promise_impl<::fpromise::internal::discard_value_continuation<promise_impl>> discard_value() {
    assert(state_.has_value());
    return make_promise_with_continuation(
        ::fpromise::internal::discard_value_continuation<promise_impl>(std::move(*this)));
  }

  // Applies a |wrapper| to the promise.  Invokes the wrapper's |wrap()|
  // method, passes the promise to the wrapper by value followed by any
  // additional |args| passed to |wrap_with()|, then returns the wrapper's
  // result.
  //
  // |Wrapper| is a type that implements a method called |wrap()| which
  // accepts a promise as its argument and produces a wrapped result of
  // any type, such as another promise.
  //
  // Asserts that the promise is non-empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  // In this example, |fpromise::sequencer| is a wrapper type that imposes
  // FIFO execution order onto a sequence of wrapped promises.
  //
  //     // This wrapper type is intended to be applied to
  //     // a sequence of promises so we store it in a variable.
  //     fpromise::sequencer seq;
  //
  //     // This task consists of some amount of work that must be
  //     // completed sequentially followed by other work that can
  //     // happen in any order.  We use |wrap_with()| to wrap the
  //     // sequential work with the sequencer.
  //     fpromise::promise<> perform_complex_task() {
  //         return fpromise::make_promise([] { /* do sequential work */ })
  //             .then([] (fpromise::result<> result) { /* this will also be wrapped */ })
  //             .wrap_with(seq)
  //             .then([] (fpromise::result<> result) { /* do more work */ });
  //     }
  //
  // This example can also be written without using |wrap_with()|.
  // The behavior is equivalent but the syntax may seem more awkward.
  //
  //     fpromise::sequencer seq;
  //
  //     promise<> perform_complex_task() {
  //         return seq.wrap(
  //                 fpromise::make_promise([] { /* sequential work */ })
  //             ).then([] (fpromise::result<> result) { /* more work */ });
  //     }
  //
  template <typename Wrapper, typename... Args>
  decltype(auto) wrap_with(Wrapper& wrapper, Args... args) {
    assert(state_.has_value());
    return wrapper.wrap(std::move(*this), std::forward<Args>(args)...);
  }

  // Wraps the promise's continuation into a |fit::function|.
  //
  // A boxed promise is easier to store and pass around than the unboxed
  // promises produced by |fpromise::make_promise()| and combinators, though boxing
  // may incur a heap allocation.
  //
  // It is a good idea to defer boxing the promise until after all
  // desired combinators have been applied to prevent unnecessary heap
  // allocation during intermediate states of the promise's construction.
  //
  // Returns an empty promise if this promise is empty.
  // This method consumes the promise's continuation, leaving it empty.
  //
  // EXAMPLE
  //
  //     // f's is a fpromise::promise_impl<> whose continuation contains an
  //     // anonymous type (the lambda)
  //     auto f = fpromise::make_promise([] {});
  //
  //     // g's type will be fpromise::promise<> due to boxing
  //     auto boxed_f = f.box();
  //
  //     // alternately, we can get exactly the same effect by assigning
  //     // the unboxed promise to a variable of a named type instead of
  //     // calling box()
  //     fpromise::promise<> boxed_f = std::move(f);
  //
  promise_impl<::fit::function<result_type(context&)>> box() { return std::move(*this); }

 private:
  template <typename>
  friend class promise_impl;

  state_type state_;
};

template <typename Continuation>
void swap(promise_impl<Continuation>& a, promise_impl<Continuation>& b) {
  a.swap(b);
}

template <typename Continuation>
bool operator==(const promise_impl<Continuation>& f, decltype(nullptr)) {
  return !f;
}
template <typename Continuation>
bool operator==(decltype(nullptr), const promise_impl<Continuation>& f) {
  return !f;
}
template <typename Continuation>
bool operator!=(const promise_impl<Continuation>& f, decltype(nullptr)) {
  return !!f;
}
template <typename Continuation>
bool operator!=(decltype(nullptr), const promise_impl<Continuation>& f) {
  return !!f;
}

// Makes a promise containing the specified continuation.
//
// This function is used for making a promises given a callable object
// that represents a valid continuation type.  In contrast,
// |fpromise::make_promise()| supports a wider range of types and should be
// preferred in most situations.
//
// |Continuation| is a callable object with the signature
// fpromise::result<V, E>(fpromise::context&).
template <typename Continuation>
inline promise_impl<Continuation> make_promise_with_continuation(Continuation continuation) {
  return promise_impl<Continuation>(std::move(continuation));
}

// Returns an unboxed promise that wraps the specified handler.
// The type of the promise's result is inferred from the handler's result.
//
// |handler| is a callable object (such as a lambda.  Must not be null.
//
// The handler must return one of the following types:
// - void
// - fpromise::result<value_type, error_type>
// - fpromise::ok<value_type>
// - fpromise::error<error_type>
// - fpromise::pending
// - fpromise::promise<value_type, error_type>
// - any callable or unboxed promise with the following signature:
//   fpromise::result<value_type, error_type>(fpromise::context&)
//
// The handler must accept one of the following argument lists:
// - ()
// - (fpromise::context&)
//
// See documentation of |fpromise::promise| for more information.
//
// SYNOPSIS
//
// |Handler| is the handler function type.  It is typically inferred by the
// compiler from the |handler| argument.
//
// EXAMPLE
//
//     enum class weather_type { sunny, glorious, cloudy, eerie, ... };
//
//     weather_type look_outside() { ... }
//     void wait_for_tomorrow(fpromise::suspended_task task) {
//         ... arrange to call task.resume_task() tomorrow ...
//     }
//
//     fpromise::promise<weather_type, std::string> wait_for_good_weather(int max_days) {
//         return fpromise::make_promise([days_left = max_days] (fpromise::context& context) mutable
//                             -> fpromise::result<int, std::string> {
//             weather_type weather = look_outside();
//             if (weather == weather_type::sunny || weather == weather_type::glorious)
//                 return fpromise::ok(weather);
//             if (days_left > 0) {
//                 wait_for_tomorrow(context.suspend_task());
//                 return fpromise::pending();
//             }
//             days_left--;
//             return fpromise::error("nothing but grey skies");
//         });
//     }
//
//     auto f = wait_for_good_weather(7)
//         .and_then([] (const weather_type& weather) { ... })
//         .or_else([] (const std::string& error) { ... });
//
template <typename PromiseHandler>
inline promise_impl<::fpromise::internal::context_handler_invoker<PromiseHandler>> make_promise(
    PromiseHandler handler) {
  static_assert(::fit::is_callable<PromiseHandler>::value,
                "PromiseHandler must be a callable object.");

  assert(!fit::is_null(handler));
  return make_promise_with_continuation(
      ::fpromise::internal::promise_continuation<PromiseHandler>(std::move(handler)));
}

// Returns an unboxed promise that immediately returns the specified result when invoked.
//
// This function is especially useful for returning promises from functions
// that have multiple branches some of which complete synchronously.
//
// |result| is the result for the promise to return.
//
// See documentation of |fpromise::promise| for more information.
template <typename V = void, typename E = void>
inline promise_impl<::fpromise::internal::result_continuation<V, E>> make_result_promise(
    fpromise::result<V, E> result) {
  return make_promise_with_continuation(
      ::fpromise::internal::result_continuation<V, E>(std::move(result)));
}
template <typename V = void, typename E = void>
inline promise_impl<::fpromise::internal::result_continuation<V, E>> make_result_promise(
    fpromise::ok_result<V> result) {
  return make_promise_with_continuation(
      ::fpromise::internal::result_continuation<V, E>(std::move(result)));
}
template <typename V = void, typename E = void>
inline promise_impl<::fpromise::internal::result_continuation<V, E>> make_result_promise(
    fpromise::error_result<E> result) {
  return make_promise_with_continuation(
      ::fpromise::internal::result_continuation<V, E>(std::move(result)));
}
template <typename V = void, typename E = void>
inline promise_impl<::fpromise::internal::result_continuation<V, E>> make_result_promise(
    fpromise::pending_result result) {
  return make_promise_with_continuation(
      ::fpromise::internal::result_continuation<V, E>(std::move(result)));
}

// Returns an unboxed promise that immediately returns the specified value when invoked.
//
// This function is especially useful for returning promises from functions
// that have multiple branches some of which complete synchronously.
//
// |value| is the value for the promise to return.
//
// See documentation of |fpromise::promise| for more information.
template <typename V>
inline promise_impl<::fpromise::internal::result_continuation<V, void>> make_ok_promise(V value) {
  return make_result_promise(fpromise::ok(std::move(value)));
}

// Overload of |make_ok_promise()| used when the value type is void.
inline promise_impl<::fpromise::internal::result_continuation<void, void>> make_ok_promise() {
  return make_result_promise(fpromise::ok());
}

// Returns an unboxed promise that immediately returns the specified error when invoked.
//
// This function is especially useful for returning promises from functions
// that have multiple branches some of which complete synchronously.
//
// |error| is the error for the promise to return.
//
// See documentation of |fpromise::promise| for more information.
template <typename E>
inline promise_impl<::fpromise::internal::result_continuation<void, E>> make_error_promise(
    E error) {
  return make_result_promise(fpromise::error(std::move(error)));
}

// Overload of |make_error_promise()| used when the error type is void.
inline promise_impl<::fpromise::internal::result_continuation<void, void>> make_error_promise() {
  return make_result_promise(fpromise::error());
}

// Jointly evaluates zero or more promises.
// Returns a promise that produces a std::tuple<> containing the result
// of each promise once they all complete.
//
// EXAMPLE
//
//     auto get_random_number() {
//         return fpromise::make_promise([] { return rand() % 10 });
//     }
//
//     auto get_random_product() {
//         auto f = get_random_number();
//         auto g = get_random_number();
//         return fpromise::join_promises(std::move(f), std::move(g))
//             .and_then([] (std::tuple<fpromise::result<int>, fpromise::result<int>>& results) {
//                 return fpromise::ok(results.get<0>.value() + results.get<1>.value());
//             });
//     }
//
template <typename... Promises>
inline promise_impl<::fpromise::internal::join_continuation<Promises...>> join_promises(
    Promises... promises) {
  return make_promise_with_continuation(
      ::fpromise::internal::join_continuation<Promises...>(std::move(promises)...));
}

// Jointly evaluates zero or more homogenous promises (same result and error
// type).  Returns a promise that produces a std::vector<> containing the
// result of each promise once they all complete.
//
// EXAMPLE
//
//     auto get_random_number() {
//         return fpromise::make_promise([] { return rand() % 10 });
//     }
//
//     auto get_random_product() {
//         std::vector<fpromise::promise<int>> promises;
//         promises.push_back(get_random_number());
//         promises.push_back(get_random_number());
//         return fpromise::join_promise_vector(std::move(promises))
//             .and_then([] (std::vector<fpromise::result<int>>& results) {
//                 return fpromise::ok(results[0].value() + results[1].value());
//             });
//     }
//
template <typename V, typename E>
inline promise_impl<::fpromise::internal::join_vector_continuation<fpromise::promise<V, E>>>
join_promise_vector(std::vector<fpromise::promise<V, E>> promises) {
  return make_promise_with_continuation(
      ::fpromise::internal::join_vector_continuation<fpromise::promise<V, E>>(std::move(promises)));
}

// Describes the status of a future.
enum class future_state {
  // The future neither holds a result nor a promise that could produce a result.
  // An empty future cannot make progress until a promise or result is assigned to it.
  empty,
  // The future holds a promise that may eventually produce a result but
  // it currently doesn't have a result.  The future's promise must be
  // invoked in order to make progress from this state.
  pending,
  // The future holds a successful result.
  ok,
  // The future holds an error result.
  error
};

// A |fpromise::future| holds onto a |fpromise::promise| until it has completed then
// provides access to its |fpromise::result|.
//
// SYNOPSIS
//
// |V| is the type of value produced when the completes successfully.
// Defaults to |void|.
//
// |E| is the type of error produced when the completes with an error.
// Defaults to |void|.
//
// THEORY OF OPERATION
//
// A future has a single owner who is responsible for setting its promise
// or result and driving its execution.  Unlike |fpromise::promise|, a future retains
// the result produced by completion of its asynchronous task.  Result retention
// eases the implementation of combined tasks that need to await the results
// of other tasks before proceeding.
//
// See the example for details.
//
// A future can be in one of four states, depending on whether it holds...
// - a successful result: |fpromise::future_state::ok|
// - an error result: |fpromise::future_state::error|
// - a promise that may eventually produce a result: |fpromise::future_state::pending|
// - neither: |fpromise::future_state_empty|
//
// On its own, a future is "inert"; it only makes progress in response to
// actions taken by its owner.  The state of the future never changes
// spontaneously or concurrently.
//
// When the future's state is |fpromise::future_state::empty|, its owner is
// responsible for setting the future's promise or result thereby moving the
// future into the pending or ready state.
//
// When the future's state is |fpromise::future_state::pending|, its owner is
// responsible for calling the future's |operator()| to invoke the promise.
// If the promise completes and returns a result, the future will transition
// to the ok or error state according to the result.  The promise itself will
// then be destroyed since it has fulfilled its purpose.
//
// When the future's state is |fpromise::future_state::ok|, its owner is responsible
// for consuming the stored value using |value()|, |take_value()|,
// |result()|, |take_result()|, or |take_ok_result()|.
//
// When the future's state is |fpromise::future_state::error|, its owner is
// responsible for consuming the stored error using |error()|, |take_error()|,
// |result()|, |take_result()|, or |take_error_result()|.
//
// See also |fpromise::promise| for more information about promises and their
// execution.
//
// EXAMPLE
//
// -
// https://fuchsia.googlesource.com/fuchsia/+/HEAD/zircon/system/utest/fit/examples/promise_example2.cc
template <typename V = void, typename E = void>
using future = future_impl<promise<V, E>>;

// Future implementation details.
// See |fpromise::future| documentation for more information.
template <typename Promise>
class future_impl final {
 public:
  // The type of promise held by the future.
  using promise_type = Promise;

  // The promise's result type.
  // Equivalent to fpromise::result<value_type, error_type>.
  using result_type = typename Promise::result_type;

  // The type of value produced when the promise completes successfully.
  // May be void.
  using value_type = typename Promise::value_type;

  // The type of value produced when the promise completes with an error.
  // May be void.
  using error_type = typename Promise::error_type;

  // Creates a future in the empty state.
  future_impl() = default;
  future_impl(decltype(nullptr)) {}

  // Creates a future and assigns a promise to compute its result.
  // If the promise is empty, the future enters the empty state.
  // Otherwise the future enters the pending state.
  explicit future_impl(promise_type promise) {
    if (promise) {
      state_.template emplace<1>(std::move(promise));
    }
  }

  // Creates a future and assigns its result.
  // If the result is pending, the future enters the empty state.
  // Otherwise the future enters the ok or error state.
  explicit future_impl(result_type result) {
    if (result) {
      state_.template emplace<2>(std::move(result));
    }
  }

  // Moves from another future, leaving the other one in an empty state.
  future_impl(future_impl&& other) : state_(std::move(other.state_)) {
    other.state_.template emplace<0>();
  }

  // Destroys the promise, releasing its promise and result (if any).
  ~future_impl() = default;

  // Returns the state of the future: empty, pending, ok, or error.
  future_state state() const {
    switch (state_.index()) {
      case 0:
        return future_state::empty;
      case 1:
        return future_state::pending;
      case 2:
        return cpp17::get<2>(state_).is_ok() ? future_state::ok : future_state::error;
    }
    __builtin_unreachable();
  }

  // Returns true if the future's state is not |fpromise::future_state::empty|:
  // it either holds a result or holds a promise that can be invoked to make
  // progress towards obtaining a result.
  explicit operator bool() const { return !is_empty(); }

  // Returns true if the future's state is |fpromise::future_state::empty|:
  // it does not hold a result or a promise so it cannot make progress.
  bool is_empty() const { return state() == fpromise::future_state::empty; }

  // Returns true if the future's state is |fpromise::future_state::pending|:
  // it does not hold a result yet but it does hold a promise that can be invoked
  // to make progress towards obtaining a result.
  bool is_pending() const { return state() == fpromise::future_state::pending; }

  // Returns true if the future's state is |fpromise::future_state::ok|:
  // it holds a value that can be retrieved using |value()|, |take_value()|,
  // |result()|, |take_result()|, or |take_ok_result()|.
  bool is_ok() const { return state() == fpromise::future_state::ok; }

  // Returns true if the future's state is |fpromise::future_state::error|:
  // it holds an error that can be retrieved using |error()|, |take_error()|,
  // |result()|, |take_result()|, or |take_error_result()|.
  bool is_error() const { return state() == fpromise::future_state::error; }

  // Returns true if the future's state is either |fpromise::future_state::ok| or
  // |fpromise::future_state::error|.
  bool is_ready() const { return state_.index() == 2; }

  // Evaluates the future and returns true if its result is ready.
  // Asserts that the future is not empty.
  //
  // If the promise completes and returns a result, the future will transition
  // to the ok or error state according to the result.  The promise itself will
  // then be destroyed since it has fulfilled its purpose.
  bool operator()(fpromise::context& context) {
    switch (state_.index()) {
      case 0:
        return false;
      case 1: {
        result_type result = cpp17::get<1>(state_)(context);
        if (!result)
          return false;
        state_.template emplace<2>(std::move(result));
        return true;
      }
      case 2:
        return true;
    }
    __builtin_unreachable();
  }

  // Gets a reference to the future's promise.
  // Asserts that the future's state is |fpromise::future_state::pending|.
  const promise_type& promise() const {
    assert(is_pending());
    return cpp17::get<1>(state_);
  }

  // Takes the future's promise, leaving it in an empty state.
  // Asserts that the future's state is |fpromise::future_state::pending|.
  promise_type take_promise() {
    assert(is_pending());
    auto promise = std::move(cpp17::get<1>(state_));
    state_.template emplace<0>();
    return promise;
  }

  // Gets a reference to the future's result.
  // Asserts that the future's state is |fpromise::future_state::ok| or
  // |fpromise::future_state::error|.
  result_type& result() {
    assert(is_ready());
    return cpp17::get<2>(state_);
  }
  const result_type& result() const {
    assert(is_ready());
    return cpp17::get<2>(state_);
  }

  // Takes the future's result, leaving it in an empty state.
  // Asserts that the future's state is |fpromise::future_state::ok| or
  // |fpromise::future_state::error|.
  result_type take_result() {
    assert(is_ready());
    auto result = std::move(cpp17::get<2>(state_));
    state_.template emplace<0>();
    return result;
  }

  // Gets a reference to the future's value.
  // Asserts that the future's state is |fpromise::future_state::ok|.
  template <typename R = value_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  R& value() {
    assert(is_ok());
    return cpp17::get<2>(state_).value();
  }
  template <typename R = value_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  const R& value() const {
    assert(is_ok());
    return cpp17::get<2>(state_).value();
  }

  // Takes the future's value, leaving it in an empty state.
  // Asserts that the future's state is |fpromise::future_state::ok|.
  template <typename R = value_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  R take_value() {
    assert(is_ok());
    auto value = cpp17::get<2>(state_).take_value();
    state_.template emplace<0>();
    return value;
  }
  ok_result<value_type> take_ok_result() {
    assert(is_ok());
    auto result = cpp17::get<2>(state_).take_ok_result();
    state_.template emplace<0>();
    return result;
  }

  // Gets a reference to the future's error.
  // Asserts that the future's state is |fpromise::future_state::error|.
  template <typename R = error_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  R& error() {
    assert(is_error());
    return cpp17::get<2>(state_).error();
  }
  template <typename R = error_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  const R& error() const {
    assert(is_error());
    return cpp17::get<2>(state_).error();
  }

  // Takes the future's error, leaving it in an empty state.
  // Asserts that the future's state is |fpromise::future_state::error|.
  template <typename R = error_type, typename = std::enable_if_t<!std::is_void<R>::value>>
  R take_error() {
    assert(is_error());
    auto error = cpp17::get<2>(state_).take_error();
    state_.template emplace<0>();
    return error;
  }
  error_result<error_type> take_error_result() {
    assert(is_error());
    auto result = cpp17::get<2>(state_).take_error_result();
    state_.template emplace<0>();
    return result;
  }

  // Move assigns from another future, leaving the other one in an empty state.
  future_impl& operator=(future_impl&& other) = default;

  // Discards the future's promise and result, leaving it empty.
  future_impl& operator=(decltype(nullptr)) {
    state_.template emplace<0>();
    return *this;
  }

  // Assigns a promise to compute the future's result.
  // If the promise is empty, the future enters the empty state.
  // Otherwise the future enters the pending state.
  future_impl& operator=(promise_type promise) {
    if (promise) {
      state_.template emplace<1>(std::move(promise));
    } else {
      state_.template emplace<0>();
    }
    return *this;
  }

  // Assigns the future's result.
  // If the result is pending, the future enters the empty state.
  // Otherwise the future enters the ok or error state.
  future_impl& operator=(result_type result) {
    if (result) {
      state_.template emplace<2>(std::move(result));
    } else {
      state_.template emplace<0>();
    }
    return *this;
  }

  // Swaps the futures' contents.
  void swap(future_impl& other) {
    using std::swap;
    swap(state_, other.state_);
  }

  future_impl(const future_impl&) = delete;
  future_impl& operator=(const future_impl&) = delete;

 private:
  cpp17::variant<cpp17::monostate, promise_type, result_type> state_;
};

template <typename Promise>
void swap(future_impl<Promise>& a, future_impl<Promise>& b) {
  a.swap(b);
}

template <typename Promise>
bool operator==(const future_impl<Promise>& f, decltype(nullptr)) {
  return !f;
}
template <typename Promise>
bool operator==(decltype(nullptr), const future_impl<Promise>& f) {
  return !f;
}
template <typename Promise>
bool operator!=(const future_impl<Promise>& f, decltype(nullptr)) {
  return !!f;
}
template <typename Promise>
bool operator!=(decltype(nullptr), const future_impl<Promise>& f) {
  return !!f;
}

// Makes a future containing the specified promise.
template <typename Promise>
future_impl<Promise> make_future(Promise promise) {
  return future_impl<Promise>(std::move(promise));
}

// A pending task holds a |fpromise::promise| that can be scheduled to run on
// a |fpromise::executor| using |fpromise::executor::schedule_task()|.
//
// An executor repeatedly invokes a pending task until it returns true,
// indicating completion.  Note that the promise's resulting value or error
// is discarded since it is not meaningful to the executor.  If you need
// to consume the result, use a combinator such as |fpromise::pending::then()|
// to capture it prior to wrapping the promise into a pending task.
//
// See documentation of |fpromise::promise| for more information.
class pending_task final {
 public:
  // The type of promise held by this task.
  using promise_type = promise<void, void>;

  // Creates an empty pending task without a promise.
  pending_task() = default;

  // Creates a pending task that wraps an already boxed promise that returns
  // |fpromise::result<void, void>|.
  pending_task(promise_type promise) : promise_(std::move(promise)) {}

  // Creates a pending task that wraps any kind of promise, boxed or unboxed,
  // regardless of its result type and with any context that is assignable
  // from this task's context type.
  template <typename Continuation>
  pending_task(promise_impl<Continuation> promise)
      : promise_(promise ? promise.discard_result().box() : promise_type()) {}

  pending_task(pending_task&&) = default;
  pending_task& operator=(pending_task&&) = default;

  // Destroys the pending task, releasing its promise.
  ~pending_task() = default;

  // Returns true if the pending task is non-empty (has a valid promise).
  explicit operator bool() const { return !!promise_; }

  // Evaluates the pending task.
  // If the task completes (returns a non-pending result), the task reverts
  // to an empty state (because the promise it holds has reverted to an empty
  // state) and returns true.
  // It is an error to invoke this method if the pending task is empty.
  bool operator()(fpromise::context& context) { return !promise_(context).is_pending(); }

  // Extracts the pending task's promise.
  promise_type take_promise() { return std::move(promise_); }

  pending_task(const pending_task&) = delete;
  pending_task& operator=(const pending_task&) = delete;

 private:
  promise_type promise_;
};

// Execution context for an asynchronous task, such as a |fpromise::promise|,
// |fpromise::future|, or |fpromise::pending_task|.
//
// When a |fpromise::executor| executes a task, it provides the task with an
// execution context which enables the task to communicate with the
// executor and manage its own lifecycle.  Specialized executors may subclass
// |fpromise::context| and offer additional methods beyond those which are
// defined here, such as to provide access to platform-specific features
// supported by the executor.
//
// The context provided to a task is only valid within the scope of a single
// invocation; the task must not retain a reference to the context across
// invocations.
//
// See documentation of |fpromise::promise| for more information.
class context {
 public:
  // Gets the executor that is running the task, never null.
  virtual class executor* executor() const = 0;

  // Obtains a handle that can be used to resume the task after it has been
  // suspended.
  //
  // Clients should call this method before returning |fpromise::pending()| from
  // the task.  See documentation on |fpromise::executor|.
  virtual suspended_task suspend_task() = 0;

  // Converts this context to a derived context type.
  template <typename Context, typename = std::enable_if_t<std::is_base_of<context, Context>::value>>
  Context& as() & {
    // TODO(https://fxbug.dev/42116590): We should perform a run-time type check here rather
    // than blindly casting.  That's why this method exists.
    return static_cast<Context&>(*this);
  }

 protected:
  virtual ~context() = default;
};

// An abstract interface for executing asynchronous tasks, such as promises,
// represented by |fpromise::pending_task|.
//
// EXECUTING TASKS
//
// An executor evaluates its tasks incrementally.  During each iteration
// of the executor's main loop, it invokes the next task from its ready queue.
//
// If the task returns true, then the task is deemed to have completed.
// The executor removes the tasks from its queue and destroys it since there
// it nothing left to do.
//
// If the task returns false, then the task is deemed to have voluntarily
// suspended itself pending some event that it is awaiting.  Prior to
// returning, the task should acquire at least one |fpromise::suspended_task|
// handle from its execution context using |fpromise::context::suspend_task()|
// to provide a means for the task to be resumed once it can make forward
// progress again.
//
// Once the suspended task is resumed with |fpromise::suspended_task::resume()|, it
// is moved back to the ready queue and it will be invoked again during a later
// iteration of the executor's loop.
//
// If all |fpromise::suspended_task| handles for a given task are destroyed without
// the task ever being resumed then the task is also destroyed since there
// would be no way for the task to be resumed from suspension.  We say that
// such a task has been "abandoned".
//
// The executor retains single-ownership of all active and suspended tasks.
// When the executor is destroyed, all of its remaining tasks are also
// destroyed.
//
// Please read |fpromise::promise| for a more detailed explanation of the
// responsibilities of tasks and executors.
//
// NOTES FOR IMPLEMENTORS
//
// This interface is designed to support a variety of different executor
// implementations.  For example, one implementation might run its tasks on
// a single thread whereas another might dispatch them on an event-driven
// message loop or use a thread pool.
//
// See also |fpromise::single_threaded_executor| for a concrete implementation.
class executor {
 public:
  // Destroys the executor along with all of its remaining scheduled tasks
  // that have yet to complete.
  virtual ~executor() = default;

  // Schedules a task for eventual execution by the executor.
  //
  // This method is thread-safe.
  virtual void schedule_task(pending_task task) = 0;
};

// Represents a task that is awaiting resumption.
//
// This object has RAII semantics.  If the task is not resumed by at least
// one holder of its |suspended_task| handles, then it will be destroyed
// by the executor since it is no longer possible for the task to make
// progress.  The task is said have been "abandoned".
//
// See documentation of |fpromise::executor| for more information.
class suspended_task final {
 public:
  // A handle that grants the capability to resume a suspended task.
  // Each issued ticket must be individually resolved.
  using ticket = uint64_t;

  // The resolver mechanism implements a lightweight form of reference
  // counting for tasks that have been suspended.
  //
  // When a suspended task is created in a non-empty state, it receives
  // a pointer to a resolver interface and a ticket.  The ticket is
  // a one-time-use handle that represents the task that was suspended
  // and provides a means to resume it.  The |suspended_task| class ensures
  // that every ticket is precisely accounted for.
  //
  // When |suspended_task::resume_task()| is called on an instance with
  // a valid ticket, the resolver's |resolve_ticket()| method is invoked
  // passing the ticket's value along with *true* to resume the task.  This
  // operation consumes the ticket so the |suspended_task| transitions to
  // an empty state.  The ticket and resolver cannot be used again by
  // this |suspended_task| instance.
  //
  // Similarly, when |suspended_task::reset()| is called on an instance with
  // a valid ticket or when the task goes out of scope on such an instance,
  // the resolver's |resolve_ticket()| method is invoked but this time passes
  // *false* to not resume the task.  As before, the ticket is consumed.
  //
  // Finally, when the |suspended_task| is copied, its ticket is duplicated
  // using |duplicate_ticket()| resulting in two tickets, both of which
  // must be individually resolved.
  //
  // Resuming a task that has already been resumed has no effect.
  // Conversely, a task is considered "abandoned" if all of its tickets
  // have been resolved without it ever being resumed.  See documentation
  // of |fpromise::promise| for more information.
  //
  // The methods of this class are safe to call from any thread, including
  // threads that may not be managed by the task's executor.
  class resolver {
   public:
    // Duplicates the provided ticket, returning a new ticket.
    // Note: The new ticket may have the same numeric value as the
    //       original ticket but should be considered a distinct instance
    //       that must be separately resolved.
    virtual ticket duplicate_ticket(ticket ticket) = 0;

    // Consumes the provided ticket, optionally resuming its associated task.
    // The provided ticket must not be used again.
    virtual void resolve_ticket(ticket ticket, bool resume_task) = 0;

   protected:
    virtual ~resolver() = default;
  };

  suspended_task() : resolver_(nullptr), ticket_(0) {}

  suspended_task(resolver* resolver, ticket ticket) : resolver_(resolver), ticket_(ticket) {}

  suspended_task(const suspended_task& other);
  suspended_task(suspended_task&& other);

  // Releases the task without resumption.
  //
  // Does nothing if this object does not hold a ticket.
  ~suspended_task();

  // Returns true if this object holds a ticket for a suspended task.
  explicit operator bool() const { return resolver_ != nullptr; }

  // Asks the task's executor to resume execution of the suspended task
  // if it has not already been resumed or completed.  Also releases
  // the task's ticket as a side-effect.
  //
  // Clients should call this method when it is possible for the task to
  // make progress; for example, because some event the task was
  // awaiting has occurred.  See documentation on |fpromise::executor|.
  //
  // Does nothing if this object does not hold a ticket.
  void resume_task() { resolve(true); }

  // Releases the suspended task without resumption.
  //
  // Does nothing if this object does not hold a ticket.
  void reset() { resolve(false); }

  // Swaps suspended tasks.
  void swap(suspended_task& other);

  suspended_task& operator=(const suspended_task& other);
  suspended_task& operator=(suspended_task&& other);

 private:
  void resolve(bool resume_task);

  resolver* resolver_;
  ticket ticket_;
};

inline void swap(suspended_task& a, suspended_task& b) { a.swap(b); }

}  // namespace fpromise

#endif  // LIB_FPROMISE_PROMISE_H_
